/**
 * Websocket 
 * 依赖 Cofing.js, MyNet.js
 */
class MySocket {
	private ws:any;
	private sessid:string;
	private dataObj:any;

	private status:SocketStatus;
	public constructor() {
		this.dataObj = {};
		this.addListeners();
	}

	private static instance:MySocket;
	public static getInstance():MySocket{
		if(!MySocket.instance){
			MySocket.instance = new MySocket();
		}
		return MySocket.instance;
	}

	private createSocket(url:string):WebSocket{
        var ws:any;
        if(!Config.isDebug){
            ws  = new WebSocket(url);
        }else{
            ws = {};
            this.login();
        }
        return ws;
    }    


	/**
	 * 创建 Websocket
	 * @apram url Websocket 地址
	 * @apram sessid ajax 登录返回的
	 */
	public init(url, sessid:string) {
		this.ws = this.createSocket(url);
		this.sessid = sessid;
		this.status = SocketStatus.disconnect;
        var self:any = this;
        this.ws.onopen = function(){
            console.log("socket connect");
            self.status = SocketStatus.connected;
			//创建 socket 链接之后，马上发送登录请求
			self.login();
        };

        this.ws.onmessage = function(event){
            // console.log(event);
            var json;
            try{
                json = JSON.parse(event.data);
            }catch(e){
                console.log("parse data error");
            }

            if(json){
                self.callback.call(self, json);
            }

        };


        this.ws.onerror = function(event){
            console.log("socket error");
            self.status = SocketStatus.disconnect;
        };

        this.ws.onclose = function(event){
            console.log("socket close");
            self.status = SocketStatus.disconnect;
        };
	
	}

	private callback(json:any):void{
		console.log(json);
		var t:any = this.dataObj[json.no];
		if(t){
			var obj:any;
			for(var i = 0; i < t.funcArr.length; i++){
				obj = t.funcArr[i];
				obj.func.call(obj.ths, json);
			}
		}
    }


	/**
	 * 添加 socket 协议回调
	 * 
	 * @param code 协议号
	 * @param func 回调函数
	 * @param thisArg 回调函数的 this 引用
	 */
	public addSocketListener(code:number, func:any, thisArg:any):void{
        var t:any = this.dataObj[code];
        if(!t){
            t = {};
            this.dataObj[code] = t;
            t.funcArr = [];
        }
		var len:number = t.funcArr.length;
		
		for(var i = 0; i < len; i++){
			var obj:any = t.funcArr[i];
			//已经添加过的，直接返回
			if(obj.func === func && obj.ths == thisArg){
				return;
			}
		}

		t.funcArr.push({func: func, ths: thisArg});
    }

	/**
	 * 移除 socket 协议回调
	 * 
	 * @param code 协议号
	 * @param func 回调函数
	 * @param thisArg 回调函数的 this 引用
	 */
	public removeSocketListener(code:number, func:any, thisArg:any):void{
        var t:any = this.dataObj[code];
        if(t){
			var arr:any = t.funcArr;
			var len:number = arr.length;
			for(var i = len - 1; i >= 0; i--){
				var obj:any = arr[i];
				if(obj.func == func && obj.ths == thisArg){
					arr.splice(i, 1);
					break;
				}
			}
			//删除没有任何回调函数的协议引用
			if(arr.length <= 0){
				delete this.dataObj[code];
			}
        }
    }

	/**
	 * 发送websocket 请求
	 * @param data {Object} 不能为空，至少是有 "no" 这个属性。如 { no: 10001} 
	 */
	public send(data:any):void{
		console.log("send", data);
		if(!Config.isDebug){
			if(data){
				var str;
				try{
					str = JSON.stringify(data);
				}catch(e){
					str = undefined;
					console.log("websocket send data json stringify error", data);
				}
				if(str){
					// if(this.status )
					this.ws.send(str);
				}
			}else{
				console.log("websocket send data is null");
			}
		}else{
			this.testSend(data);
		}
	}

	

	/** 测试使用 */
	private testSend(obj:any):void{
		var url:string;
		switch(obj.no){
			case SocketMsg.loginReq:
				url = "./fake/socket_login.json";
				break;
			case SocketMsg.getRoomInfoReq:
				url = "./fake/socket_room_info.json";
				break;
		}

		MyNet.get(url, this.callback, this);
	}

	private addListeners():void{
		this.addSocketListener(SocketMsg.loginRes, this.onLoginRes, this);
		this.addSocketListener(SocketMsg.kickRes, this.onKick, this);
	}

	/**
	 * 登录 socket
	 */
	private login():void{
		var obj:any = {};
		obj.no = SocketMsg.loginReq;
		obj.sessid = this.sessid;
		this.send(obj);
	}

	private onLoginRes(data:any):void{
		console.log("socket login");
		this.status = SocketStatus.login;
	}

	private onKick():void{
		this.status = SocketStatus.disconnect;
		this.ws.onopen = null;
		this.ws.onmessage = null;
		this.ws.onerror = null;
		this.ws.onclose = null;

	}

	/** 主动退出请求 */
	public exit():void{
		var obj:any = {};
		obj.no = SocketMsg.kickReq;
		this.send(obj);
	}

	public isLogin():boolean{
		return this.status === SocketStatus.login;
	}

}


const enum SocketMsg{
    /** 登录请求 */
    loginReq = 10001,
    /** 登录返回 */
    loginRes = 20001,
    
    /** 心跳请求 */
    hbReq = 10002,
    /** 心跳返回 */
    hbRes = 20002,

    /** 踢请求 */
    kickReq = 10003,
    /** 踢返回 */
    kickRes = 20003,
    /** 踢推送 */
    kickNty = 50003,

    /** 创建房间请求 */
    createRoomReq = 10004,
    /** 创建房间返回 */
    createRoomRes = 20004,

    /** 加入房间请求 */
    joinRoomReq = 10005,
    /** 加入房间返回 */
    joinRoomRes = 20005,
    /** 加入房间返回 */
    joinRoomNty = 50005,

	/** 获取房间信息请求 */
	getRoomInfoReq = 10009,
	/** 获取房间信息返回 */
	getRoomInfoRes = 20009,

    /** 准备请求 */
    readyReq = 10006,
    /** 准备推送 */
    readyNty = 50006,

    /** 加入房间严重请求 */
    authReq = 10007,
    /** 加入房间严重返回 */
    authRes = 20007,

    /** 抢庄请求 */
    bankerReq = 10008,
    /** 抢庄返回 */
    bankerRes = 20008,
    /** 抢庄推送 */
    bankerNty = 50008,

	/** 处理进房申请请求，type 1 同意；2 拒绝 */
	handleJoinRoomReq = 10010,
	/** 处理进房申请推送，只推送给房主和申请人 */
	handleJoinRoomNty = 50010,

	/** 倍数确定请求 */
    timesReq = 10011,
    /** 倍数确定推送 */
    timesNty = 50011,

	/** 发牌推送 */
	sendCardNty = 50012,

	/** 游戏开始 */
	startGameReq = 10013,
	/** 游戏开始 */
	startGameNty = 50013,

	/** 取消游戏推送 */
	canelGameNty = 50014,

	/** 游戏结算推送 */
	resultGameNty = 50015,

	/** 游戏开始了推送，进入抢庄阶段 */
	gameStartedNty = 50016,

	/** 抢庄阶段结束，进入选择倍数阶段，这个阶段完成后，会推送发牌协议 */
	bankerFinishNty = 50017,
}


const enum SocketStatus{
    /** 没链接 */
    disconnect = 1,

    /** 链接上 */
    connected = 2,

    /** 已经登录 */
    login = 3
}